package Threads;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.Enumeration;
import java.util.GregorianCalendar;
import java.util.Hashtable;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.BlockingQueue;

import Debug.Debug;
import Enums.Type_log;
import Enums.Type_rsp;
import Structs.Log;
import Structs.Member;
import Structs.Msg_broad;
import Structs.Msg_rsp;
import Structs.Task;

public class S_TCP_in_out implements Runnable{

	private Socket client;
	private Hashtable<String,Member> Clients;
	private Hashtable<Integer,Task> Tasks;
	private Hashtable<String,Object> OnlineClients;
	//private 
	private BlockingDeque<Log> log;
	private BlockingQueue<Msg_broad> Broadcast;
	private String clientname;
	BlockingQueue <Thread> GrimReaper;
	private Thread Mylife;
	private int task;
	
	public S_TCP_in_out(Socket client,
						BlockingQueue<Thread> GrimReaper,
						Hashtable Clients, 
						Hashtable Tasks,
						BlockingQueue<Msg_broad> requests, 
						BlockingDeque<Log> log,
						Hashtable OnlineClients) {
		// TODO Auto-generated constructor stub
		this.client = client;
		this.Clients = Clients;
		this.Tasks = Tasks;
		this.GrimReaper = GrimReaper;
		this.Broadcast = requests;
		this.log = log;
		this.OnlineClients = OnlineClients;
		Mylife = new Thread(this);
		Mylife.start();
	}
	
	@Override
	public void run() {
		// TODO Auto-generated method stub
		boolean logged = false;
		int DeathCalledMe = 1;
		ObjectInputStream in;
		ObjectOutputStream out;
		Msg_rsp msg=null;
		Msg_rsp build_msg = null;
		String [] info = {"",""};
		
		try{
			
			in = new ObjectInputStream(client.getInputStream());
			out = new ObjectOutputStream(client.getOutputStream());
			
			do{
		
				msg =(Msg_rsp) in.readObject();
				if(logged)
					Broadcast.add(new Msg_broad(Type_rsp.Ack, out, msg,null));
				System.out.println("bla");
				
				
				
				switch(msg.getType()){
				
				case Login:
					System.out.println("[Client][LOGIN]: login and pass{\n"+msg.debug()+"}");
					info = msg.UnpackLogin(msg.getName());
					if(CanLogin(info[0], info[1], out)){
						logged=true;
						build_msg =  new Msg_rsp(Type_rsp.Login, "True", 0);
					}
					else
						build_msg =  new Msg_rsp(Type_rsp.Login, "", 0);
					//CanLogin(info[0],info[1], );
					break;
				case Register:
					System.out.println("[Client][REGISTER]: register{\n"+msg.debug()+"}");
					info = msg.UnpackLogin(msg.getName());
					if(CanRegister(info[0],info[1]) && CanLogin(info[0], info[1], out)){
						System.out.println("true");
						logged=true;
						build_msg =  new Msg_rsp(Type_rsp.Register, "True", 0);
					}
					else
						build_msg =  new Msg_rsp(Type_rsp.Register, "", 0);
					break;
				case Logout:
					System.out.println("[Client][LOGOUT]: Logout{\n"+msg.debug()+"}");
					if(CanLogout(info[0])){
						System.out.println("true");
						logged=false;
						build_msg =  new Msg_rsp(Type_rsp.Logout, "True", 0);
					}
					else{
						build_msg =  new Msg_rsp(Type_rsp.Error, "", 0);
					}
					break;
				case NewTask:
					System.out.println("[Client][TASK]: NewTask{\n"+msg.debug()+"}");
					if(CanCreateTask(msg.getName(), Clients.get(info[0]), msg.getModif())){
						build_msg =  new Msg_rsp(Type_rsp.NewTask, "Task Created", task);
					}else{
						build_msg =  new Msg_rsp(Type_rsp.Error, "No created Task Created", 0);
					}
					break;
				case EditTask:
					System.out.println("[Client][TASK]: EditTask{\n"+msg.debug()+"}");
					if(CanEditTask(msg.getId(), msg.getName(), msg.getModif())){
						build_msg =  new Msg_rsp(Type_rsp.EditTask, "Task edited", task);
					}else{
						build_msg =  new Msg_rsp(Type_rsp.Error, "Not edited or invalid Task ", 0);
					}
					break;
				case FinishTask:
					System.out.println("[Client][TASK]: FinishTask{\n"+msg.debug()+"}");
					if(CanFinishTask(msg.getId())){
						build_msg =  new Msg_rsp(Type_rsp.FinishTask, "Task Finished", task);
					}else{
						build_msg =  new Msg_rsp(Type_rsp.Error, "Not finished or invalid Task ", 0);
					}
					break;
				case ListTasks:
					//System.out.println("[Client][TASK]: ListTasks{\n"+msg.debug()+"}");
					build_msg =  new Msg_rsp(Type_rsp.ListTasks, TasksToString(), 0);
					break;
				default:
					if(new Debug().isDebug())
						System.out.println("bode default in out");
				}
				
				
				if(Clients.containsKey(info[0]))
					Broadcast.add(new Msg_broad(msg.getType(), out, build_msg,Clients.get(info[0])));
				else
					Broadcast.add(new Msg_broad(msg.getType(), out, build_msg,null));
			}while(true);
		
		} catch (IOException e){
			
			if(new Debug().isDebug()){
				System.out.println("IO Error In_out");
				e.printStackTrace();
			}
			if(OnlineClients.containsKey(info[0])){
				OnlineClients.remove(info[0]);
			}
			log.add(new Log(Type_log.Logout, info[0]));
			
		} catch (ClassNotFoundException e){
		
			if(new Debug().isDebug())
				System.out.println("class not found Error");
		
		} finally {
				GrimReaper.add(Mylife);
			
		}
	}
	
	
	
	
	private boolean CanLogin(String User,String Pass,ObjectOutputStream out){
		
		if(Clients.containsKey(User)){
		
			if(Clients.get(User).getPass().equals(Pass)){
				OnlineClients.put(User, out);
				return true;
			}
		}
		
		return false;
	}
	
	private boolean CanRegister (String User,String Password){
		
		if (!Clients.containsKey(User)){
		
			Clients.put(User, new Member(User, Password));
			return true;
	
		}
		
		return false;
	}
	
	private boolean CanLogout(String User){

		if (OnlineClients.containsKey(User)){
		
			OnlineClients.remove(User);
			log.add(new Log(Type_log.Logout, User));
			return true;
		}
		
		return false;
	}
	
	private boolean CanCreateTask(String Titulo,Member User,GregorianCalendar Date){
		
		Enumeration<Integer> id_e = Tasks.keys();
		Integer id=0;
		
		while(id_e.hasMoreElements()){
			id = id_e.nextElement();
		}
		
		task=id+1;
		
		Task t = new Task(Titulo,User,Date);
		Tasks.put(id+1, t);
		return true;
	}
	
	
	private boolean CanFinishTask(Integer id){
		
		task=id;
		
		if(Tasks.containsKey(id)){
			Tasks.get(id).setComplete(true);
			return true;
		}
		
		return false;
	}
	
	private boolean CanEditTask(Integer Id,String Titulo,GregorianCalendar Date ){
		
		task=Id.intValue();
		
		if(Tasks.containsKey(Id)){
		
			Tasks.get(Id).setTitulo(Titulo);
			Tasks.get(Id).setModified(Date);
			return true;
		}
		
		return false;
	}
	
	private String TasksToString(){
		String message = "";
		Enumeration<Integer> id_e = Tasks.keys();
		Integer id;
		Task temp;
		if(!Tasks.isEmpty()){
			while(id_e.hasMoreElements()){
			
				id = id_e.nextElement();
				if(!Tasks.get(id).isComplete())
					message = message+Tasks.get(id).toMessageString(id)+"\n";

			}
		} else {
			message = "no tasks in the server";
		}
		
		
		return message;
	}
}
